package uk.ac.cam.cl.projectcharlie2010.clients.desktop.gui.gamesetup;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.ButtonGroup;

import uk.ac.cam.cl.projectcharlie2010.clients.desktop.controller.DesktopController;
import uk.ac.cam.cl.projectcharlie2010.game.GameController;
import uk.ac.cam.cl.projectcharlie2010.game.player.Player;
import uk.ac.cam.cl.projectcharlie2010.game.player.PlayerEngine;
import uk.ac.cam.cl.projectcharlie2010.game.player.PlayerHuman;
import uk.ac.cam.cl.projectcharlie2010.game.player.physical.PlayerPhysical;

import ch.nostromo.lib.controller.NosResourcesException;
import ch.nostromo.lib.controller.NosSettingsException;
import ch.nostromo.lib.swing.NosButton;
import ch.nostromo.lib.swing.NosLabel;
import ch.nostromo.lib.swing.NosPanel;
import ch.nostromo.lib.swing.NosTextField;
import ch.nostromo.lib.swing.NosToggleButton;
import ch.nostromo.tiffanys.clients.desktop.gui.board.BoardPanel;
import ch.nostromo.tiffanys.clients.desktop.gui.settings.EngineSettingsPanel;
import ch.nostromo.tiffanys.game.GameControllerException;
import ch.nostromo.tiffanys.game.player.PlayerException;
import ch.nostromo.tiffanys.game.player.PlayerTools;

public class GameSetupPanel extends NosPanel {

	private static final long serialVersionUID = 1L;

	private static Logger logger = Logger.getLogger(GameSetupPanel.class.getName());

	private GameController currentGameController = null;
	private NosLabel lblPlayerWhite;
	private NosButton btnWhiteSettings;
	private BoardPanel boardPanel;
	private ButtonGroup blackButtonGroup;
	private NosButton btnFen;
	private NosButton nosButton1;
	private NosButton btnStartNewGame;
	private ButtonGroup whiteButtonGroup;
	private NosButton btnBlackSettings;
	private NosTextField txtPlayerBlackName;
	private NosToggleButton btnBlackEngine;
	private NosToggleButton btnBlackHuman;
	private NosToggleButton btnBlackPhysical;
	private NosLabel lblPlayerBlack;
	private NosTextField txtFen;
	private NosTextField txtPlayerWhiteName;
	private NosToggleButton btnWhiteEngine;
	private NosToggleButton btnWhiteHuman;
	private NosToggleButton btnWhitePhysical;

	private NosButton btnUpdateBackground;

	public GameSetupPanel(GameController gameController, boolean newGame) throws PlayerException, NosResourcesException, CloneNotSupportedException {
		DesktopController.getInstance().getCurrentGameController().getPlayerBlack().kill();
		DesktopController.getInstance().getCurrentGameController().getPlayerWhite().kill();

		//NOTE: This cannot be left till the closeAction because the new game will already have been started
		DesktopController.getInstance().getCurrentGameController().getPlayerBlack().setActive(true); //To allow finish/death
		DesktopController.getInstance().getCurrentGameController().getPlayerWhite().setActive(true); //To allow finish/death

		initGUI();
		initNOS(gameController, newGame);
	}



	//NOTE: Not used, as far as I can tell using Eclipse search
	//	public GameSetupPanel() {
	//		initGUI();
	//	}


	private void initGUI() {

		{
			this.setPreferredSize(new java.awt.Dimension(588, 420)); //388 -> 420
			this.setLayout(null);
			this.setSize(588, 420); //388 -> 420
			{
				lblPlayerWhite = new NosLabel();
				this.add(lblPlayerWhite);
				lblPlayerWhite.setText("Player White");
				lblPlayerWhite.setBounds(9, 285, 93, 15);
				lblPlayerWhite.setToolTipTranslate(true);
				lblPlayerWhite.setTextTranslationKey("Setup.PlayerWhite");
			}
			{
				btnWhiteHuman = new NosToggleButton();
				this.add(btnWhiteHuman);
				btnWhiteHuman.setText("H");
				btnWhiteHuman.setBounds(108, 282, 50, 22);
			}
			{
				btnWhiteEngine = new NosToggleButton();
				this.add(btnWhiteEngine);
				btnWhiteEngine.setText("T");
				btnWhiteEngine.setBounds(164, 282, 50, 22);
			}
			{
				btnWhitePhysical = new NosToggleButton();
				this.add(btnWhitePhysical);
				btnWhitePhysical.setText("P");
				btnWhitePhysical.setBounds(219, 282, 50, 22);
			}
			{
				txtPlayerWhiteName = new NosTextField();
				this.add(txtPlayerWhiteName);
				txtPlayerWhiteName.setBounds(282, 282, 120, 22);
				//				txtPlayerWhiteName.setBounds(226, 282, 196, 22);
			}
			{
				btnWhiteSettings = new NosButton();
				this.add(btnWhiteSettings);
				btnWhiteSettings.setText("Engine Settings");
				btnWhiteSettings.setBounds(428, 282, 147, 22);
				btnWhiteSettings.setTextTranslationKey("Setup.EngineSettings");
				btnWhiteSettings.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent evt) {
						whiteSettingsAction();
					}
				});
			}
			{
				boardPanel = new BoardPanel();
				this.add(boardPanel);
				boardPanel.setBounds(152, 54, 177, 178);
			}
			{
				txtFen = new NosTextField();
				this.add(txtFen);
				txtFen.setBounds(109, 244, 467, 22);
			}
			{
				lblPlayerBlack = new NosLabel();
				this.add(lblPlayerBlack);
				lblPlayerBlack.setText("Player Black");
				lblPlayerBlack.setBounds(9, 21, 93, 15);
				lblPlayerBlack.setTextTranslationKey("Setup.PlayerBlack");
			}
			{
				btnBlackHuman = new NosToggleButton();
				this.add(btnBlackHuman);
				btnBlackHuman.setText("H");
				btnBlackHuman.setBounds(108, 18, 50, 22);
			}
			{
				btnBlackEngine = new NosToggleButton();
				this.add(btnBlackEngine);
				btnBlackEngine.setText("T");
				btnBlackEngine.setBounds(164, 18, 50, 22);
			}
			{
				btnBlackPhysical = new NosToggleButton();
				this.add(btnBlackPhysical);
				btnBlackPhysical.setText("P");
				btnBlackPhysical.setBounds(219, 18, 50, 22);
			}
			{
				txtPlayerBlackName = new NosTextField();
				this.add(txtPlayerBlackName);
				txtPlayerBlackName.setBounds(282, 18, 120, 22);
			}
			{
				btnBlackSettings = new NosButton();
				this.add(btnBlackSettings);
				btnBlackSettings.setText("Engine Settings");
				btnBlackSettings.setBounds(428, 18, 147, 22);
				btnBlackSettings.setToolTipTranslate(true);
				btnBlackSettings.setTextTranslationKey("Setup.EngineSettings");
				btnBlackSettings.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent evt) {
						blackSettingsAction();
					}
				});
			}
			{
				btnStartNewGame = new NosButton();
				this.add(btnStartNewGame);
				btnStartNewGame.setText("New game");
				btnStartNewGame.setBounds(108, 325, 161, 29);
				btnStartNewGame.setTextTranslate(false);
				btnStartNewGame.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent evt) {
						startGameAction();
					}
				});
			}
			{
				nosButton1 = new NosButton();
				this.add(nosButton1);
				nosButton1.setText("Abort");
				nosButton1.setBounds(414, 325, 161, 29);
				nosButton1.setTextTranslationKey("Common.Cancel");
				nosButton1.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent evt) {
						closeByCancelAction();
					}
				});
			}
			{
				btnUpdateBackground = new NosButton();
				this.add(btnUpdateBackground);
				btnUpdateBackground.setText("Calibrate Camera");
				btnUpdateBackground.setBounds(414, 370, 161, 29);
				btnUpdateBackground.setTextTranslationKey("Common.CalibrateCamera");
				btnUpdateBackground.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent evt) {
						calibrateCamera();
						//FIXME RAT
					}
				});
			}
			{
				btnFen = new NosButton();
				this.add(btnFen);
				btnFen.setText("FEN");
				btnFen.setBounds(9, 244, 74, 22);
				btnFen.setTextTranslationKey("Setup.Fen");
				btnFen.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent evt) {
						fenAction();
					}
				});
			}
			this.blackButtonGroup = new ButtonGroup();
			this.whiteButtonGroup = new ButtonGroup();

			this.blackButtonGroup.add(this.btnBlackEngine);
			btnBlackEngine.setTextTranslationKey("Setup.Engine");
			btnBlackEngine.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					switchBlackToEngine();
				}
			});
			this.blackButtonGroup.add(this.btnBlackHuman);
			btnBlackHuman.setTextTranslationKey("Setup.Human");
			btnBlackHuman.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					switchBlackToHuman();
				}
			});
			this.blackButtonGroup.add(this.btnBlackPhysical);
			btnBlackPhysical.setTextTranslationKey("Setup.Physical");
			btnBlackPhysical.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					switchBlackToPhysical();
				}
			});

			this.whiteButtonGroup.add(this.btnWhiteEngine);
			btnWhiteEngine.setTextTranslationKey("Setup.Engine");
			btnWhiteEngine.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					switchWhiteToEngine();
				}
			});
			this.whiteButtonGroup.add(this.btnWhiteHuman);
			btnWhiteHuman.setTextTranslationKey("Setup.Human");
			btnWhiteHuman.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					switchWhiteToHuman();
				}
			});
			this.whiteButtonGroup.add(this.btnWhitePhysical);
			btnWhitePhysical.setTextTranslationKey("Setup.Physical");
			btnWhitePhysical.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent evt) {
					switchWhiteToPhysical();
				}
			});

		}

	}

	protected void calibrateCamera() {
		DesktopController.getInstance().calibrateCamera();
	}

	public void initNOS(GameController gameController, boolean newGame) throws PlayerException, NosResourcesException, CloneNotSupportedException {

		// Update board gfx
		this.remove(boardPanel);
		this.boardPanel = new BoardPanel(null);
		this.add(boardPanel);
		boardPanel.setBounds(108, 54, 177, 178);

		this.currentGameController = gameController.clone();


		if (newGame) {
			this.btnFen.setEnabled(true);
			this.btnStartNewGame.setText(DesktopController.getInstance().getTranslation("Setup.NewGame"));
		} else {
			this.btnFen.setEnabled(false);
			this.btnStartNewGame.setText(DesktopController.getInstance().getTranslation("Setup.ContinueGame"));
		}

		this.btnFen.setEnabled(newGame);

		// Player white
		//CHANGED: currentGameController instead of gameController!
		Player white = currentGameController.getPlayerWhite();
		this.txtPlayerWhiteName.setText(white.getPlayerName());

		// Player black
		//CHANGED: currentGameController instead of gameController!
		//NOTE: getPlayerName changes the engine for PlayerEngines 
		Player black = currentGameController.getPlayerBlack();

		this.txtPlayerBlackName.setText(black.getPlayerName());
		//How is it that this changes the 


		this.setInputStatus();

		this.txtFen.setText(gameController.getFEN());
		this.boardPanel.setBoard(gameController.getCurrentBoard());



	}

	private void setInputStatus() throws PlayerException {

		// Player white
		Player white = currentGameController.getPlayerWhite();
		this.txtPlayerWhiteName.setText(white.getPlayerName());
		if (white instanceof PlayerEngine) {
			this.btnWhiteSettings.setEnabled(true);
			this.txtPlayerWhiteName.setEnabled(false);
			this.btnWhiteEngine.setSelected(true);
			//			this.btnWhiteHuman.setSelected(false);
			//			unnecessary? they're already in the same button group
		} else if (white instanceof PlayerPhysical){
			this.btnWhiteSettings.setEnabled(false);
			this.txtPlayerWhiteName.setEnabled(true);
			this.btnWhitePhysical.setSelected(true);
		} else {
			this.btnWhiteSettings.setEnabled(false);
			this.txtPlayerWhiteName.setEnabled(true);
			//			this.btnWhiteEngine.setSelected(false);
			this.btnWhiteHuman.setSelected(true);
		}

		Player black = currentGameController.getPlayerBlack();
		this.txtPlayerBlackName.setText(black.getPlayerName());
		if (black instanceof PlayerEngine) {
			this.btnBlackSettings.setEnabled(true);
			this.txtPlayerBlackName.setEnabled(false);
			this.btnBlackEngine.setSelected(true);
			//			this.btnBlackHuman.setSelected(false);
			//see above
		} else if (black instanceof PlayerPhysical){
			this.btnBlackSettings.setEnabled(false);
			this.txtPlayerBlackName.setEnabled(true);
			this.btnBlackPhysical.setSelected(true);
		} else {
			this.btnBlackSettings.setEnabled(false);
			this.txtPlayerBlackName.setEnabled(true);
			//			this.btnBlackEngine.setSelected(false);
			this.btnBlackHuman.setSelected(true);
		}

	}

	void switchWhiteToEngine() {
		try {
			currentGameController.setPlayerWhite(DesktopController.getInstance().getDefaultPlayerEngine());
			this.setInputStatus();
		} catch (PlayerException e) {
			logger.log(Level.SEVERE, "Player exception", e);
		} catch (NosSettingsException e) {
			logger.log(Level.SEVERE, "Settings exception", e);
		}
	}

	void switchWhiteToHuman() {
		try {
			currentGameController.setPlayerWhite(DesktopController.getInstance().getDefaultPlayerHuman());
			this.setInputStatus();
		} catch (PlayerException e) {
			logger.log(Level.SEVERE, "Player exception", e);
		} catch (NosResourcesException e) {
			logger.log(Level.SEVERE, "Resources exception", e);
		}
	}

	void switchWhiteToPhysical() {
		try {
			currentGameController.setPlayerWhite(DesktopController.getInstance().getDefaultPlayerPhysical());
			this.setInputStatus();
		} catch (PlayerException e) {
			logger.log(Level.SEVERE, "Player exception", e);
		} catch (NosResourcesException e) {
			logger.log(Level.SEVERE, "Resources exception", e);
		}
	}

	void switchBlackToEngine() {
		try {
			currentGameController.setPlayerBlack(DesktopController.getInstance().getDefaultPlayerEngine());
			this.setInputStatus();
		} catch (PlayerException e) {
			logger.log(Level.SEVERE, "Player exception", e);
		} catch (NosSettingsException e) {
			logger.log(Level.SEVERE, "Settings exception", e);
		}
	}

	void switchBlackToHuman() {
		try {
			currentGameController.setPlayerBlack(DesktopController.getInstance().getDefaultPlayerHuman());
			this.setInputStatus();
		} catch (PlayerException e) {
			logger.log(Level.SEVERE, "Player exception", e);
		} catch (NosResourcesException e) {
			logger.log(Level.SEVERE, "Resources exception", e);
		}
	}

	void switchBlackToPhysical() {
		try {
			currentGameController.setPlayerBlack(DesktopController.getInstance().getDefaultPlayerPhysical());
			this.setInputStatus();
		} catch (PlayerException e) {
			logger.log(Level.SEVERE, "Player exception", e);
		} catch (NosResourcesException e) {
			logger.log(Level.SEVERE, "Resources exception", e);
		}
	}

	void closeByCancelAction() {
		
		//We're cancelling, so use the original game controller!
		try {
			currentGameController = DesktopController.getInstance().getCurrentGameController().clone();
		} catch (CloneNotSupportedException e) {
			System.err.println("Caught clone exception. BAD!");
			e.printStackTrace();
		}
		
		try {
			//CHANGED: New methods for physical
			Player oldWhite = currentGameController.getPlayerWhite();
			if (oldWhite instanceof PlayerPhysical) {
				currentGameController.setPlayerWhite(PlayerTools.createPlayerPhysical(oldWhite.getPlayerName(), DesktopController.getInstance()));
			} else if (oldWhite instanceof PlayerHuman) {
				currentGameController.setPlayerWhite(PlayerTools.createPlayerHuman(oldWhite.getPlayerName()));
			} else { //Engine
				currentGameController.setPlayerWhite(PlayerTools.createPlayerEngine(DesktopController.getInstance().getSettings().DEFAULT_ENGINE_NAME, ((PlayerEngine) oldWhite).getEngineSettings()));
			}

			Player oldBlack = currentGameController.getPlayerBlack();
			if (oldBlack instanceof PlayerPhysical) {
				currentGameController.setPlayerBlack((PlayerTools.createPlayerPhysical(oldBlack.getPlayerName(), DesktopController.getInstance())));
			} else if (oldBlack instanceof PlayerHuman) {
				currentGameController.setPlayerBlack(PlayerTools.createPlayerHuman(oldBlack.getPlayerName()));
			} else { //Engine
				currentGameController.setPlayerBlack(PlayerTools.createPlayerEngine(DesktopController.getInstance().getSettings().DEFAULT_ENGINE_NAME, ((PlayerEngine) oldBlack).getEngineSettings()));
			}
		} catch (PlayerException e) {
			System.err.println("Caught player exception. BAD!");
			e.printStackTrace();
		}

		DesktopController.getInstance().launchNewGame(currentGameController);

		this.nosClose();
	}

	void whiteSettingsAction() {
		try {
			EngineSettingsPanel esp = new EngineSettingsPanel(((PlayerEngine) currentGameController.getPlayerWhite()).getEngineSettings());
			String title = DesktopController.getInstance().getTranslation("EngineSettings.Title");
			DesktopController.getInstance().getDesktop().showPanelAsDialog(esp, title);
		} catch (NosResourcesException e) {
			logger.log(Level.SEVERE, "Resources exception", e);
		}
	}

	void blackSettingsAction() {
		try {
			EngineSettingsPanel esp = new EngineSettingsPanel(((PlayerEngine) currentGameController.getPlayerBlack()).getEngineSettings());
			String title = DesktopController.getInstance().getTranslation("EngineSettings.Title");
			DesktopController.getInstance().getDesktop().showPanelAsDialog(esp, title);
		} catch (NosResourcesException e) {
			logger.log(Level.SEVERE, "Resources exception", e);
		}
	}

	void fenAction() {
		try {
			String fen = txtFen.getText();
			Player white = currentGameController.getPlayerWhite();
			Player black = currentGameController.getPlayerBlack();
			currentGameController = new GameController(white, black, fen);
			this.boardPanel.setBoard(currentGameController.getCurrentBoard());
		} catch (GameControllerException e) {
			logger.log(Level.SEVERE, "GameControler exception", e);
		}
	}

	void startGameAction() {
			


			//Why do this:
			//If new game is started, we kill old players to avoid them making invalid moves.
			//Hence, we must create new players 

			//CHANGED: New methods for physical
			Player oldWhite = currentGameController.getPlayerWhite();
			if (oldWhite instanceof PlayerPhysical) {
				currentGameController.setPlayerWhite(PlayerTools.createPlayerPhysical(txtPlayerWhiteName.getText(), DesktopController.getInstance()));
			} else if (oldWhite instanceof PlayerHuman) {
				currentGameController.setPlayerWhite(PlayerTools.createPlayerHuman(txtPlayerWhiteName.getText()));
			} else { //Engine
				currentGameController.setPlayerWhite(PlayerTools.createPlayerEngine(DesktopController.getInstance().getSettings().DEFAULT_ENGINE_NAME, ((PlayerEngine) oldWhite).getEngineSettings()));
			}

			Player oldBlack = currentGameController.getPlayerBlack();
			if (oldBlack instanceof PlayerPhysical) {
				currentGameController.setPlayerBlack((PlayerTools.createPlayerPhysical(txtPlayerWhiteName.getText(), DesktopController.getInstance())));
			} else if (oldBlack instanceof PlayerHuman) {
				currentGameController.setPlayerBlack(PlayerTools.createPlayerHuman(txtPlayerWhiteName.getText()));
			} else { //Engine
				currentGameController.setPlayerBlack(PlayerTools.createPlayerEngine(DesktopController.getInstance().getSettings().DEFAULT_ENGINE_NAME, ((PlayerEngine) oldBlack).getEngineSettings()));
			}

			DesktopController.getInstance().launchNewGame(currentGameController);

			this.nosClose();
	}

}
